using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using Visitor;
using Taste;

namespace ASTClass
{
    public enum Operator
    {
        // ======================= Expr Operators =====================
        // FunctorAST operator
        Negate, 
        
        // AddExprAST operations
        Add, Subtract, 
        
        // MulExprAST operations
        Multiply, Divide, Exponent, Modulo,
        
        // RelExpr operations
        Equal, NotEqual, LessEqual, Less, Greater, GreaterEqual,

        // ============================================================

        // FuncOrAssignAST operations
        Assign, Func, 

        NoOp

    };         

    public class AST
    {
       
        public static int id = 0;

        #region Fields

        protected List<AST> children = new List<AST>();
        protected List<Operator> operators = new List<Operator>();
        protected AST parent = null;
        protected int oid;
        protected string name = "un_defined";

        #endregion Fields

        #region Constructors and properties

        public AST() { oid = id++; }
        public AST(AST p) 
        { 
            parent = p;
            oid = id++;

            if (parent != null)
                parent.AddChild(this);   
        }
       
        public string Name 
        { 
            get { return name; }
            set { name = value; }
        }

        public virtual string UniqueName 
        { 
            get { return Name + oid.ToString(); }
        }

        public List<AST> Children
        {
            get { return children; }
        }

        public List<Operator> Operators
        {
            get { return operators; }
        }

        public AST Parent { get { return parent; } }

        #endregion Constructors and properties

        #region Basic AST utilities

        public virtual AST Clone() 
        {
            AST ast = new AST();
            ast.operators = operators;
            foreach (AST c in children)
                ast.children.Add(c.Clone());
            return ast;
        }

        public void SetParent(AST p) { parent = p; }
        public void AddChild(AST child)
        {
            children.Add(child);
        }
        public void RemoveChild(AST child)
        {
            children.Remove(child);
        }
        public AST GetChild(int index)
        {
            return children[index];
        }
        public bool HasOperators()
        {
            if (operators.Count > 0) return true;
            return false;
        }
        public bool HasChildren()
        {
            if (children.Count > 0) return true;
            return false;
        }
        public int ChildCount() { return children.Count; }
  
        public void ReparentChildren(AST parent) 
        {
            foreach (AST c in children)
                c.SetParent(parent);

            children.Clear();
        }
        public void AddOp(Operator op)
        {
            operators.Add(op);
        }

        #endregion Basic AST utilities

        #region AST semantics check and simplification

        public virtual bool SemanticCheck()
        {
            foreach (AST c in children)
                if (!c.SemanticCheck())
                    return false;

            return true;
        }

        public void Simplify()
        {
            for (int i = 0; i < children.Count; i++)
            {
                AST c = children[i];
                if (!c.HasOperators() && c.ChildCount() == 1)
                {
                    // Node has one child, so it must be index 0
                    children[i] = c.GetChild(0);
                    children[i].SetParent(this);
                    c.SetParent(null); c = null;
                    --i;
                }
            }

            // Remove non-Terminal leaf node child
            List<AST> newchildren = new List<AST>();
            bool changed = false;
            foreach (AST c in children)
            {
                if (!c.HasOperators() && c.ChildCount() == 0)
                {
                    // Node has no child; remove it if it is not a Termial type
                    TerminalAST t = c as TerminalAST;
                    if (t == null)
                    {
                        changed = true;
                        c.SetParent(null);
                    }
                }
                else
                    newchildren.Add(c);
            }
            if (changed) children = newchildren;

            foreach (AST c in children)
                c.Simplify();
        }

        #endregion AST semantics check and simplification

        #region  Methods to make graphviz dot file for AST display

        public bool MakeDotFile(string fileName, string title)
        {
            FileStream file;
            StreamWriter stream = null;

            try
            {
                file = new FileStream(fileName, FileMode.Create, FileAccess.Write);
            }
            catch
            {
                return false;
            }
            stream = new StreamWriter(file);

            stream.WriteLine("digraph G {");
            stream.WriteLine("Title [label=\"" + title + "\",shape=box]");
            Dump(stream);
            stream.Write("}");

            if (stream != null)
            {
                stream.Close();
            }

            return true;
        }

        public virtual void Dump(StreamWriter sw)
        {
            sw.Write(UniqueName + "[label = \"" + Name);
            if (operators.Count > 0)
            {
                sw.Write(" |");
                foreach (Operator i in operators)
                    sw.Write(i.ToString() + " ");
                sw.Write("|");
            }
            sw.Write("\"]\r\n");
            foreach (AST child in children)
            {
                sw.WriteLine(UniqueName + " -> " + child.UniqueName);
                child.Dump(sw);
            }
        }

        #endregion  Methods to make graphviz dot file for AST display

        #region Visitor pattern

        public virtual void Accept(IVisitor visitor)
        {
            visitor.Visit(this);
        }


        #endregion Visitor pattern

        public virtual void CodeGen(SymbolTable st, CodeFileGen cfg)
        {

        }
    }
}